package ControllerApplication;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import ModelApplication.Sound;
import ProcessingAudioWindows.AudioProcessingWindowLocal;
import ProcessingAudioWindows.W2AudioProcessingWindow;
import UtilsApplication.ChartTypes;
import UtilsApplication.HandleAudioUtil;
import UtilsApplication.Parameters;

public class W2AudioProcessing extends AudioProcessing {

    public W2AudioProcessing( String methodName, AudioProcessingWindowLocal imgWindow ) {
        super( methodName, imgWindow );
    }
    @Override
    public List<Sound> processingList(Sound sound) {

        W2AudioProcessingWindow imgW = (W2AudioProcessingWindow) imgWindow;
        List<Sound> sounds = new ArrayList<Sound>();
        double [] origAudio = sound.getAudio();

        double [] audio = Arrays.copyOfRange( origAudio, 0, origAudio.length );
        double [] output = new double[origAudio.length];

        int len = imgW.getWindowWidth();
        len = 150;
        final int M = 10;
//253
        long start = System.currentTimeMillis();
        for(int i = 0; i < audio.length - 1; i += len) {

            double [] audioArray = Arrays.copyOfRange( audio, i, len + i );

            double [] coeff = new double[M];
            double err = analyze(audioArray, coeff, M);          
            double [] tmpOutput = synthesise( coeff, len, Arrays.copyOfRange( audioArray, 0, M ));

            for(int j = 0; j < len && i + j < output.length; j++) {
                output[i + j] = tmpOutput[j];
            }

        }

        Sound outhSound = new Sound(output, sound.getWavFile(), null);
        outhSound.setExecutionTime( (System.currentTimeMillis() - start) / 1000.0 );
        sounds.add(outhSound );

        Sound synthSound = new Sound(HandleAudioUtil.normalizeSingal( output), sound.getWavFile(), null);
        synthSound.setExecutionTime( (System.currentTimeMillis() - start) / 1000.0 );
        sounds.add(synthSound );

        return sounds;
    }
    
    /**
     * Returns minimum mean square error.
     * @param lpc - float[0...p-1] LPC coefficients
     * @param ac -  in: float[0...p] autocorrelation values
     * @param ref - out: float[0...p-1] reflection coef's
     * @param p
     * @return minimum mean square error.
     */
    public static double wld(final double[] lpc,
                            final double[] ac,
                            final double[] ref,
                            final int p)
    {
      int i, j;
      double r, error = ac[0];
      if (ac[0] == 0) {
        for (i=0; i<p; i++)
          ref[i] = 0;
        return 0;
      }
      for (i = 0; i < p; i++) {
        /* Sum up this iteration's reflection coefficient. */
        r = -ac[i + 1];
        for (j = 0; j < i; j++) r -= lpc[j] * ac[i - j];
        ref[i] = r /= error;
        /*  Update LPC coefficients and total error. */
        lpc[i] = r;
        for (j = 0; j < i/2; j++) {
          double tmp  = lpc[j];
          lpc[j]     += r * lpc[i-1-j];
          lpc[i-1-j] += r * tmp;
        }
        if ((i % 2) != 0)
          lpc[j] += lpc[j] * r;
        error *= 1.0 - r * r;
      }
      return error;
    }



    double analyze(double [] audioArray, double [] rc, int m) {

        double [] r = new double[m + 1];

        autocorr( audioArray, m,  r );
        //lag_window( m,  r);


        double [] a = new double[m ];

      //  double err = levinson(r, a, rc, m);

//        for(int i = 0; i < m; i++) {
//            rc[i] = a[i];
//        }

     //   rc = getARFilter(r, m);
       // double [] b = new double[m];
        
        wld(a,r, rc,m );
        
//        for(int i = 0; i < m; i++) {
//            rc[i]= a[i];
//        }
        
        return 0;

    }


    double [] synthesise(double [] rc, int len, double [] input) {

        double [] out = new double[len];

        for(int i = 0; i < rc.length; i++) {      
            out[i] = input[i]; 
        }

        for(int i = rc.length; i < len; i++) {

            double x = 0;
            for(int j = 1; j < rc.length; j++) {
                x += rc[j] * out[i - j];
            }


            out[i] = x; 
        }





        return out;

    }
    
    
    /**
     * Method to compute Linear Prediction Coefficients for a frame of speech. Assumes the following 
     * sign convention:<br> prediction(x[t]) = Sum_i {Ar[i] * x[t-i]}
     *
     * @param autocor
     * @return the energy of the frame (alpha in the Levinson recursion)
     */
    public double[] getARFilter(double[] autocor, int order) {
        /* No signal */
        if (autocor[0] == 0) {
            return null;
        }
        double [] reflectionCoeffs = new double[order + 1];
        double [] ARParameters = new double[order + 1];
        double[] backwardPredictor = new double[order + 1];

        double alpha = autocor[0];
        reflectionCoeffs[1] = -autocor[1] / autocor[0];
        ARParameters[0] = 1.0;
        ARParameters[1] = reflectionCoeffs[1];
        alpha *= (1 - reflectionCoeffs[1] * reflectionCoeffs[1]);

        for (int i = 2; i <= order; i++) {
            for (int j = 1; j < i; j++) {
                backwardPredictor[j] = ARParameters[i - j];
            }
            reflectionCoeffs[i] = 0;
            for (int j = 0; j < i; j++) {
                reflectionCoeffs[i] -= ARParameters[j] * autocor[i - j];
            }
            reflectionCoeffs[i] /= alpha;

            for (int j = 1; j < i; j++) {
                ARParameters[j] += reflectionCoeffs[i] * backwardPredictor[j];
            }
            ARParameters[i] = reflectionCoeffs[i];
            alpha *= (1 - reflectionCoeffs[i] * reflectionCoeffs[i]);
            if (alpha <= 0.0) {
                return null;
            }
        }
        return ARParameters;
    }




    /*----------------------------------------------------------------------------
     * autocorr - compute the auto-correlations of windowed speech signal
     *----------------------------------------------------------------------------
     */
    void autocorr(
            double []x,              /* input : input signal x[0:L_WINDOW] */
            int m,                 /* input : LPC order                  */
            double []r               /* output: auto-correlation vector r[0:M]*/
            )
    {
        double y[] = new double[x.length];  
        double sum;
        int i, j;


        for (i = 0; i < x.length; i++)
            y[i] = x[i]*Parameters.hamwindow[i%240];

        for (i = 0; i <= m; i++)
        {
            sum = (double)0.0;
            for (j = 0; j < x.length-i; j++)
                sum += y[j]*y[j+i];
            r[i] = sum;
        }
        if (r[0]<(double)1.0) r[0]=(double)1.0;

        return;
    }

    /*-------------------------------------------------------------*
     * procedure lag_window:                                       *
     *           ~~~~~~~~~                                         *
     * lag windowing of the autocorrelations                       *
     *-------------------------------------------------------------*/

    void lag_window(
            int m,                 /* input : LPC order                  */
            double   r[]            /* in/out: correlation */
            )
    {
        int i;

        for (i=1; i<= m; i++)
            r[i] *= Parameters.lwindow[(i-1)%10];

        return;
    }


    /*----------------------------------------------------------------------------
     * levinson - levinson-durbin recursion to compute LPC parameters
     *----------------------------------------------------------------------------
     */
    double levinson(         /* output: prediction error (energy) */
            double []r,              /* input : auto correlation coefficients r[0:M] */
            double []a,              /* output: lpc coefficients a[0] = 1 */
            double []rc,              /* output: reflection coefficients rc[0:M-1]    */
            final int m
            )
    {
        double s, at, err;
        int i, j, l;

        rc[0] = (-r[1])/r[0];
        a[0] = (double)1.0;
        a[1] = rc[0];
        err = r[0] + r[1]*rc[0];
        for (i = 2; i <= m; i++)
        {
            s = (double)0.0;
            for (j = 0; j < i; j++)
                s += r[i-j]*a[j];
            rc[i-1]= (-s)/(err);
            for (j = 1; j <= (i/2); j++)
            {
                l = i-j;
                at = a[j] + rc[i-1]*a[l];
                a[l] += rc[i-1]*a[j];
                a[j] = at;
            }
            a[i] = rc[i-1];
            err += rc[i-1]*s;
            if (err <= (double)0.0)
                err = (double)0.001;
        }
        return (err);
    }
}
